package com.df.ez_game.server.logic.status_sync.controllers;

import com.df.ez_game.EZGameCtx;
import com.df.ez_game.business.annotation.EzAuth;
import com.df.ez_game.business.annotation.EzController;
import com.df.ez_game.business.annotation.EzMapping;
import com.df.ez_game.server.EzAttribute;
import com.df.ez_game.server.logic.status_sync.SyncPushMsgEnum;
import com.df.ez_game.server.logic.status_sync.SyncExceptions;
import com.df.ez_game.server.logic.status_sync.controllers.entity.EnterMapReq;
import com.df.ez_game.server.logic.status_sync.controllers.entity.MoveReq;
import com.df.ez_game.server.logic.status_sync.controllers.entity.MoveResp;
import com.df.ez_game.server.logic.status_sync.controllers.entity.PlayerData;
import com.df.ez_game.server.logic.status_sync.controllers.entity.PlayerState;
import com.df.ez_game.server.logic.status_sync.controllers.entity.TimeSyncReq;
import com.df.ez_game.server.logic.status_sync.controllers.entity.TimeSyncResp;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@EzController(id = 1)
public class SyncController {
    public static EzAttribute<PlayerData> playerAttr = EzAttribute.create("player");
    // 使用ConcurrentHashMap提高并发安全性
    public static Map<Long, PlayerData> users = new ConcurrentHashMap<>();
    
    // 服务器当前逻辑帧
    private static long serverFrame = 0;
    
    // 服务器帧率（毫秒）
    private static final long FRAME_INTERVAL = 100; // 10帧/秒
    
    static {
        startMapThread();
    }

    private static void startMapThread() {
        Thread t = new Thread(()->{
            mapUpdate();
        });
        t.setDaemon(true);
        t.start();
    }
    
    private static void mapUpdate(){
        //服务器1秒10帧
        long lastUpdateTime = System.currentTimeMillis();
        long nextUpdateTime = lastUpdateTime + FRAME_INTERVAL;
        
        while (true){
            long now = System.currentTimeMillis();
            if(now < nextUpdateTime){
                try {
                    Thread.sleep(nextUpdateTime - now);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            
            long deltaTime = now - lastUpdateTime;
            lastUpdateTime = now;
            nextUpdateTime = now + FRAME_INTERVAL;
            
            mapUpdateDo(deltaTime);
        }
    }

    private static void mapUpdateDo(long deltaTime) {
        // 递增服务器帧号
        serverFrame++;
        
        // 遍历所有玩家，更新他们的位置
        for (PlayerData player : users.values()) {
            // 只有在移动状态时才更新位置
            if (player.isMoving()) {
                // 更新玩家位置
                player.updatePosition(deltaTime);
                
                // 检查是否需要广播玩家状态（位置发生显著变化时）
                if (player.needBroadcast()) {
                    broadcastPlayerState(player);
                }
            }
        }
    }
    
    // 广播玩家状态给其他玩家
    private static void broadcastPlayerState(PlayerData player) {
        EZGameCtx context = EZGameCtx.getContext();
        List<Long> otherPlayers = users.keySet().stream()
                .filter(id -> !id.equals(player.getId())).toList();
        
        MoveResp moveResp = new MoveResp();
        moveResp.setId(player.getId());
        moveResp.setX(player.getX());
        moveResp.setY(player.getY());
        moveResp.setServerFrame(serverFrame);
        moveResp.setTimestamp(System.currentTimeMillis());
        
        if (!otherPlayers.isEmpty()) {
            context.broadcast(otherPlayers, SyncPushMsgEnum.Move.getCmd(), moveResp);
        }
        
        // 重置广播标志
        player.resetBroadcastFlag();
    }

    @EzMapping(id = 1, desc = "进入地图")
    public PlayerData enterMap(EnterMapReq req) {
        Long id = req.getPlayerId();
        if (users.containsKey(id)) {
            throw SyncExceptions.playerExist;
        }
        
        PlayerData playerData = new PlayerData();
        playerData.setId(id);
        playerData.setX(req.getStartX() );
        playerData.setY(req.getStartY() );
        playerData.setLastMoveTimestamp(System.currentTimeMillis());
        
        users.put(id, playerData);
        
        //上下文绑定id
        EZGameCtx context = EZGameCtx.getContext();
        context.login(id);
        context.setAttr(playerAttr, playerData);
        
        // 通知玩家进入成功，并发送当前地图上的其他玩家信息
        MoveResp moveResp = new MoveResp();
        moveResp.setId(id);
        moveResp.setX(playerData.getX());
        moveResp.setY(playerData.getY());
        moveResp.setServerFrame(serverFrame);
        moveResp.setTimestamp(System.currentTimeMillis());
        
        context.pushSelf(SyncPushMsgEnum.Move.getCmd(), moveResp);
        
        // 向其他玩家广播新玩家加入
        broadcastPlayerState(playerData);
        
        return playerData;
    }

    @EzAuth()
    @EzMapping(id = 2, desc = "移动")
    public MoveResp move(MoveReq req) {
        EZGameCtx context = EZGameCtx.getContext();
        PlayerData playerData = context.getAttr(playerAttr);
        
        if (playerData == null) {
            throw SyncExceptions.playerNotExist;
        }
        
        // 记录客户端发送的时间戳和帧号，用于计算延迟
        long clientTimestamp = req.getTimestamp();
        long clientFrame = req.getClientFrame();
        
        // 更新玩家移动状态和方向
        boolean wasMoving = playerData.isMoving();
        playerData.setDX(req.getDx());
        playerData.setDY(req.getDy());
        playerData.setMoving(req.isMoving());
        playerData.setLastMoveTimestamp(System.currentTimeMillis());
        playerData.setLastClientFrame(clientFrame);
        
        // 计算网络延迟
        long currentTime = System.currentTimeMillis();
        long latency = currentTime - clientTimestamp;
        playerData.updateAverageLatency(latency);
        
        // 创建响应
        MoveResp moveResp = new MoveResp();
        moveResp.setId(playerData.getId());
        moveResp.setX(playerData.getX());
        moveResp.setY(playerData.getY());
        moveResp.setServerFrame(serverFrame);
        moveResp.setTimestamp(currentTime);
        moveResp.setClientFrame(clientFrame);
        
        // 只有在移动状态改变时才广播给其他玩家
        if (wasMoving != playerData.isMoving() || Math.abs(req.getDx()) > 0.1 || Math.abs(req.getDy()) > 0.1) {
            broadcastPlayerState(playerData);
        }
        
        // 返回服务器确认给客户端，用于客户端回滚
        return moveResp;
    }
    
    @EzAuth()
    @EzMapping(id = 3, desc = "时间同步")
    public TimeSyncResp syncTime(TimeSyncReq req) {
        TimeSyncResp resp = new TimeSyncResp();
        resp.setRequestTimestamp(req.getRequestTimestamp());
        resp.setServerTimestamp(System.currentTimeMillis());
//        resp.setServerTimestamp(serverFrame);
        
        return resp;
    }
    
    @EzAuth()
    @EzMapping(id = 4, desc = "离开地图")
    public void leaveMap() {
        EZGameCtx context = EZGameCtx.getContext();
        Long playerId = context.getLoginId();
        
        if (playerId != null && users.containsKey(playerId)) {
            users.remove(playerId);
            
            // 通知其他玩家该玩家离开
            List<Long> otherPlayers = users.keySet().stream()
                    .filter(id -> !id.equals(playerId)).toList();
            
            if (!otherPlayers.isEmpty()) {
                Map<String, Object> leaveMsg = new HashMap<>();
                leaveMsg.put("id", playerId);
                context.broadcast(otherPlayers, SyncPushMsgEnum.PlayerLeave.getCmd(), leaveMsg);
            }
        }
    }
}
